TODO: fix explanations, clean up code
Linear Time Invariant, or LTI processes have three properties.
The first 2 properties state that if the input(s) to an LTI process is multiplied ($\times$) or summed ($+$), then the output of the system will also be multiplied or summed.
The third property states that the process doesn't change over time. The process will behave the same and produce the same output whether an input happens earlier or later in time.
Further Information: Control Systems Lectures - LTI Systems by Brian Douglas
%matplotlib inline
import control
import numpy as np
import matplotlib.pyplot as plt
T = np.linspace(0,10,100)
sys = control.tf([1], [2,1])
sys
1 ------- 2 s + 1
U_1 = np.zeros(len(T))
U_1[10] = 1
t, y_1, _ = control.forced_response(sys, T, U_1)
plt.subplot(2, 1, 1)
plt.plot(t,y_1)
plt.subplot(2, 1, 2)
plt.step(t,U_1, 'r')
[<matplotlib.lines.Line2D at 0x1147df390>]
U_2 = np.zeros(len(T))
U_2[40] = 1
t, y_2, _ = control.forced_response(sys, T, U_2)
plt.subplot(2, 1, 1)
plt.plot(t,y_2)
plt.subplot(2, 1, 2)
plt.step(t,U_2, 'r')
[<matplotlib.lines.Line2D at 0x1180247b8>]
Let's see what happens when we add up our $U$ and $y$ vectors.
U = U_1 + U_2
y = y_1 + y_2
plt.subplot(2, 1, 1)
plt.plot(t,y)
plt.plot(t,y_1, 'y--')
plt.plot(t,y_2, 'y--')
plt.subplot(2, 1, 2)
plt.step(t,U, 'r')
[<matplotlib.lines.Line2D at 0x11770c470>]
Let's check to see if we get the same thing by calling forced_response
with both inputs.
U_check = np.zeros(len(T))
U_check[10] = 1
U_check[40] = 1
t, y_check, _ = control.forced_response(sys, T, U_check)
plt.subplot(2, 1, 1)
plt.plot(t,y_check)
plt.plot(t,y)
plt.subplot(2, 1, 2)
plt.step(t,U_check, 'r')
[<matplotlib.lines.Line2D at 0x1173cedd8>]
The lines overlap, looks good!
Let's take another look at the additive property.
# Start by importing our animation libraries
%config InlineBackend.figure_formats = {'svg',}
import matplotlib.pylab as plt
import matplotlib.gridspec as gridspec
from IPython.display import display
from matplotlib import animation
from IPython.display import HTML
U_list = []
y_list = []
U_combined = np.zeros(len(T))
for i in range(1,3):
U = np.zeros(len(T))
U[i*(20)] = 1
U_list.append(U)
t, yout, _ = control.forced_response(sys, T, U)
y_list.append(yout)
U_combined += U
t, Y_combined, _ = control.forced_response(sys, T, U_combined)
# Create sub plots
gs = gridspec.GridSpec(2,1)
f = plt.figure(figsize=(8,6));
ax1 = plt.subplot(gs[0, 0]); # row 0, col 0
ax2 = plt.subplot(gs[1, 0],sharex=ax1); # row 1, col 0
plt.setp(ax1.get_xticklabels(), visible=False)
output_lines = []
for i in range(len(U_list)):
output_line, = ax1.plot(t, y_list[i], lw=1.0, linestyle='--');
output_lines.append(output_line)
combined_output, = ax1.plot(t, Y_combined, lw=1.5, color='k');
combined_action, = ax2.step(t, U_combined, lw=1.5, linestyle='-', color='r');
ax1.set_ylabel('Output')
ax2.set_ylabel('Action')
ax2.set_xlabel('Time (s)')
# How many data points to skip for plotting
skip = 1
# The animation function. This is called sequentially, frame by frame, to create the animated plot.
def drawframe(n):
for i in range(len(U_list)):
output_lines[i].set_data(t[0:n*skip:skip], y_list[i][0:n*skip:skip])
combined_output.set_data(t[0:n*skip:skip], Y_combined[0:n*skip:skip])
combined_action.set_data(t[0:n*skip:skip], U_combined[0:n*skip:skip])
# Print out the current frame using "\r", the 'carriage return' character, as our end character.
# This makes Python print the frame on the same line.
print("Frame: {:0d}".format(n), end="\r")
return (line1,line2)
# The `interval` parameter is the delay between frames in milliseconds and it controls the speed of the animation.
anim = animation.FuncAnimation(f, drawframe, frames=round(len(t)/skip), interval=50)
HTML(anim.to_jshtml())
Frame: 99